객체지향 캡슐화 그것이 알고 싶다.

Posted by Yungwang Ryu on 2019-07-31

캡슐화란?

변경 될 수 있는 것은 어떤 것이라도 감춘다.

반사적으로 생각나는 단어는 “변경” 입니다.
다시 말해 캡슐화를 잘 되어 있는 설계 일수록 응집도는 높고 결합도는 낮습니다. 각 객체들은 스스로 역활과 책임을 충실히 하기 때문에 기능이 변경되어도 수정 할 수 있는 부분은 최소한으로 할 수 있습니다. 즉, 캡슐화란 변경 될 수 있는 부분은 최대한 외부에 노출시키지 않고 협력에 의한 인터페이스로만 소통을 하는 것으로 객체지향 설계를 할 때 핵심중에 핵심이라 할 수 있죠.

그럼 캡슐화를 지키지 못한 객체 코드를 보면서 캡슐화에 중요성에 대해서 직접 느껴 보죠

너무 많은 참견을 하는 객체

우선 극장 이라는 클래스를 봐보죠

관객이 초대권이 있냐 없냐에 따라서 분기되고 있으며 중요한 부분은 아래 코드 입니다.

Ticket ticket = ticketSeller.getTicketOffice().getTicket();

TicketSeller -> getTicketOffice -> getTicket 객체를 다리 건너 건너 티켓을 가져오고 있는데 이것에 문제점은 캡슐화를 적절하게 하지 못해 변경에 굉장히 취약하다는 점입니다. Theater 객체에서 enter 역활을 관람객을 입장시키면 되는 것입니다. 근데 티켓을 가져오기 위해 건너 건너 참조하여 데이터를 가져와서 setTicket 하는 행위는 Theater에 역활이 아닙니다. 이렇게 역활이 아닌 Theater 객체에서 코드를 사용한 것처럼 여기 저기 사용하게 된다면 관련 코드 변경이 발생 할 시 여기 저기 코드를 수정해야 하고 이것은 변경에 취약한 설계가 되는 것이죠

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Theater {
private TicketSeller ticketSeller;

public Theater(TicketSeller ticketSeller) {
this.ticketSeller = ticketSeller;
}

public void enter(Audience audience) {
if (audience.getBag().hasInvitation()) {
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().setTicket(ticket);
} else {
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}

그럼 어떻게 해야 돼?

지적했던 코드는 Theater 에서가 아닌 TicketSeller 객체에서 하는 것이죠.
이것이 바로 협력이라는 문맥을 바탕으로 적절한 역활을 할 만한 객체를 찾는 행위가 되는 것입니다. 즉 “Ticket을 판매한다.” 라는 책임을 수행할 객체를 TicketSeller에게 할당한 것이죠 그러니 TicketSeller가 그 역활과 책임을 자율적으로 잘~~ 해주면 됩니다.

코드를 보죠

1
2
3
4
5
6
7
8
9
10
11
public class Theater {
private TicketSeller ticketSeller;

public Theater(TicketSeller ticketSeller) {
this.ticketSeller = ticketSeller;
}

public void enter(Audience audience) {
ticketSeller.sellTo();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TicketSeller {

private TicketOffice ticketOffice;

public TicketSeller(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}

public void sellTo(Audience audience) {
if (audience.getBag().hasInvitation()) {
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().setTicket(ticket);
} else {
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}

가장 중요한 것은 Theater 객체에서는 더이상 TicketOffice 객체를 몰라도 이전 기능을 그데로 수행 한다는 것입니다.
TicketOffice 객체를 모른다는 것은 TicketOffice 객체가 변경 되어도 영향이 미치치 않는다는 것입니다.

자, 바로 이부분에서 캡슐화를 잘해야 하는 이유가 들어나게 되네요.
캡슐화를 잘 하는 객체는 곧 변경되는 요구사항을 반영해도 의존하는 코드가 적어 코드 변경을 최소화 하게끔 만들어 줍니다.

이것은 곧 객체지향 프로그래밍에 지향점이 될 수도 있겠네요. 객체지향 프로그래밍은 변경에 강한 프로그래밍 기법이니까요.